• mynote-front
  • mynote-front coninuous build
  • mynote front 部署
  • linux 脚本 汇总
  • windows
  • mynote front servers
  • layout
  • default.vue
  • index.vue
  • 图片等比例缩放 scaleImg
  • mynote-front my config nuxt.config.my.js
  • mynote front design
  • mynote front element-ui
  • element-ui 主题
  • form 表单验证 ----
  • nick name
  • login
  • register
  • elformValidate
  • mynote front kbp
  • 到后台服务器的网络请求监控
  • ssr时
  • 非ssr时
  • mynote front ssr
  • 填坑记录
  • router navigation guard
  • 轮播图 盗链 缓存问题
  • blur 时输入丢失问题
  • input 点击问题;padding problem
  • loading 闪烁 问题
  • npx 启动问题
  • 搜索
  • 图片转储
  • <a> 标签嵌套问题
  • 没有使用 ssr 造成的闪烁问题
  • domEvent scrollLoad 触底加载
  • iconfont
  • 注意
  • 新增、修改
  • 图标串了:
  • login 状态检查,checkNotLogin checkLogin 需要配合 ssr
  • getUserInfo getMyInfo 获取用户信息
  • el-dialog lock-scroll 问题 + html 样式问题
  • html 样式问题研究 2021-7-2
  • axios
  • mynote front debugger
  • mynote-front

    mynote-front coninuous build

    mynote front continuous build 
    lv1= lv2= type=

    mynote front 部署

    E:\xunwu\projects\官网\xunwu\README.md
    nuxt <[--
    问题:使用 nuxt start,至少要 npm install nuxt 这个很大、很多文件
    --]>
    nodejs linux 安装 
    node-sass 
    npm config 
    cd --front--install-path--
    npm config set registry https://registry.npm.taobao.org
    npm config set sass_binary_site https://npm.taobao.org/mirrors/node-sass/
    npm install --user=0
    npx nuxt build
    npm install -g pm2
    不用pm2,启动(production mode)命令为 npx nuxt start,因此,pm2 启动为(-- 后面的参数会传递给 npx 命令):
    pm2 start npx -- nuxt start
    pm2 delete npx
    最后copy favicon

    linux 脚本 汇总

    # 上传 node 安装包、front git 压缩包 到 /root/init
    cd /root/init
    # 解压 front
    # 修改 my.js 配置,注意 baseURL 配置
    -- node 安装 nodejs linux 安装 
    cd /home/www/front
    npm config set registry https://registry.npm.taobao.org
    npm config set sass_binary_site https://npm.taobao.org/mirrors/node-sass/
    npm install --user=0
    npm install -g pm2
    npx nuxt build
    pm2 start npx -- nuxt start
    # 虚拟内存
    cd /usr
    mkdir swap
    cd swap/
    dd if=/dev/zero of=/usr/swap/swapfile bs=1M count=2048
    mkswap /usr/swap/swapfile
    swapon /usr/swap/swapfile
    # 设置开机自动启用虚拟内存,在etc/fstab文件中加入如下命令
    # 使用vim编辑器打开/etc/fstab文件
    echo "/usr/swap/swapfile swap swap defaults 0 0" >> /etc/fstab

    windows

    〖E:\uninote\deploy-windows〗

    mynote front servers

    layout

    default.vue

    /**
     * 在页面加载前先获取 localStorage 中的用户信息
     */
    mounted() {
      this.$store.dispatch('auth/getUserInfo');
    }
    <template>
      <div class="template-container">
        <v-header></v-header>
        <nuxt/>
      </div>
    </template>

    index.vue

    .editorconfig
    detect indent
    mynote-front 
    :ssr时在服务器端执行,浏览器不执行。在用户手动路由时,会执行。
    lv1= lv2= type=

    图片等比例缩放 scaleImg

    align center 居中 
    SHA-1: 2f1374818427b85c70657c4a47e303fcd64ee308
    * [feat]: 首页文章缩略图等比例缩放
    naturalWidth naturalHeight
    load 事件
    __uninote_scaled__ 缓存
    mode:fill full
    container 需要 relative、abs等 position
    组件的mounted updated 都需要监听
    问题:
    通过 ref 拿到组件,但没法追加监听 mounted/updated ?
    lv1= lv2= type=

    mynote-front my config nuxt.config.my.js

    因为是嵌套结构,所以用这种方式更方便:
    // load custom config
    var fs = require("fs");
    let path = __dirname + '/nuxt.config.my.js';
    if (fs.existsSync(path)) {
        myconfig = require(path);
        myconfig(module.exports);
    }
    config.axios.baseURL = config.proxy["/api"] = "http://localhost:222/";
    ---- nuxt.config.my.js for online
    // 直接修改默认配置
    module.exports = function (config) {
      config.axios.baseURL = config.proxy["/api"] = "http://127.0.0.1:85/";
      config.build.extend = function (config, ctx) {
        config.devtool = 'source-map';
      }
      config.server.port = 8888;
    }
    ---- nuxt.config.my.js for test
    // 直接修改默认配置
    module.exports = function (config) {
      config.axios.baseURL = config.proxy["/api"] = "http://dev.rongyipiao.com/";
      config.build.extend = function (config, ctx) {
        config.devtool = 'none';
      }
      config.server.port = 80;
    }
    ---- for test in one server
    // 直接修改默认配置
    module.exports = function (config) {
      config.proxy["/api"] = "http://127.0.0.1:85/";
      config.axios.baseURL = config.proxy["/api"] = "http://127.0.0.1:85";
      config.build.extend = function (config, ctx) {
        config.devtool = 'none';
      }
      config.server.port = 80;
    }
    ---- for test local
    // 直接修改默认配置
    module.exports = function (config) {
      config.axios.baseURL = config.proxy["/api"] = "http://192.168.0.121/";
      config.build.extend = function (config, ctx) {
        config.devtool = 'source-map';
      }
      config.server.port = 3333;
    }

    mynote front design

    mynote design 
    lv1= lv2= type=

    mynote front element-ui

    Nuxt中ElementUI的使用
    https://www.cnblogs.com/shenyf/p/8361049.html
    https://blog.csdn.net/Calla_Lj/article/details/86592491
    nuxt.config.js
    css: [
      'element-ui/lib/theme-chalk/index.css',
      '~/assets/stylus/main.styl'
    ],
    assets/stylus/variable.styl 全局公共变量

    element-ui 主题

    定制主题 theme 
    assets/element-variables.scss element ui 主题修改 
    c1ecf1c94d7881e2ea8fc0472d7bbc89dc74574b
    lv1= lv2= type=

    form 表单验证 ----

    单一验证,全部验证,部分验证,返回 promise
    form 表单验证 ---- 

    nick name

    /**
     * 验证用户名合法性
     */
    validateNickname(cb) {
      return this.$refs['ruleForm'].validateField("nickname", (errMsg) => {

    login

    async login() {
      this.$refs['ruleFormRef'].validate(async valid => {
        if (valid) {

    register

    // 此时不验证图形验证码
    let p = elformValidate(this.$refs["ruleFormRef"], ['nick', 'tel', 'tel_code', 'password']);

    elformValidate

    /**
     * 封装 el-form 的验证,返回 promise
     */
    export const elformValidate = (form, fields) => {
    lv1= lv2= type=

    mynote front kbp

    到后台服务器的网络请求监控

    http.js:
    function request(url, options, cb) {
      return new ClientRequest(url, options, cb);
    }
    E:\uninote\mynote-front\node_modules\@nuxt\server\dist\server.js
    const nuxtMiddleware = ({ options, nuxt, renderRoute, resources }) => async function nuxtMiddleware(req, res, next) {
      // Get context
      const context = utils.getContext(req, res);
      const url = decodeURI(req.url);
      res.statusCode = 200;
      try {
        const result = await renderRoute(url, context);
        await nuxt.callHook('render:route', url, result, context);
    /**
     * Dispatch a request
     *
     * @param {Object} config The config specific for this request (merged with this.defaults)
     */
    Axios.prototype.request = function request(config) {
      /*eslint no-param-reassign:0*/
      // Allow for axios('example/url'[, config]) a la fetch API
      if (typeof config === 'string') {
        config = utils.merge({
          url: arguments[0]
        }, arguments[1]);
      }
      config = utils.merge(defaults, {method: 'get'}, this.defaults, config);
    net.js:
    function connect(...args) {
      var normalized = normalizeArgs(args);
      var options = normalized[0];
      debug('createConnection', normalized);
      var socket = new Socket(options);
      if (options.timeout) {
        socket.setTimeout(options.timeout);
      }
      return socket.connect(normalized);
    }

    ssr时

    E:\uninote\mynote-front-test\node_modules\follow-redirects\index.js
          assert.equal(options.protocol, protocol, "protocol mismatch");
          debug("options", options);
          return new RedirectableRequest(options, callback);
        };
    E:\uninote\mynote-front\node_modules\follow-redirects\index.js
      // Create the native request
      var request = this._currentRequest =
            nativeProtocol.request(this._options, this._onNativeResponse);
    console.log(this._options);
      this._currentUrl = url.format(this._options);
    E:\uninote\mynote-front\node_modules\axios\lib\core\Axios.js
    Axios.prototype.request = function request(config) {
      /*eslint no-param-reassign:0*/
      // Allow for axios('example/url'[, config]) a la fetch API
      if (typeof config === 'string') {
        config = utils.merge({
          url: arguments[0]
        }, arguments[1]);
      }

    非ssr时

    E:\uninote\mynote-front-test\node_modules\http-proxy\lib\http-proxy\passes\web-incoming.js
    console.log("req:", req.hostname, req.path, "--->", options.hostname);
    var proxyReq = (options.target.protocol === 'https:' ? https : http).request(
      common.setupOutgoing(options.ssl || {}, options, req)
    );
    lv1= lv2= type=

    mynote front ssr

    mynote front kbp 
    nuxt ssr 
    E:\uninote\mynote-front-test\node_modules\@nuxtjs\axios\lib\module.js
      // Set _AXIOS_BASE_URL_ for dynamic SSR baseURL
      process.env._AXIOS_BASE_URL_ = options.baseURL
    最终被生成到了这里:
    E:\uninote\mynote-front-test\.nuxt\axios.js
    export default (ctx, inject) => {
      // baseURL
      const baseURL = process.browser
          ? '/'
          : (process.env._AXIOS_BASE_URL_ || 'http://192.168.0.121/')

    填坑记录

    collin 将 ssr 的代理 baseURL 配置删除了,因此 ssr 代理的 base 由默认值决定。部署的 test1 正常:
    axios.js 生成为:
      // baseURL
      const baseURL = process.browser
          ? '/'
          : (process.env._AXIOS_BASE_URL_ || 'http://localhost:80/')
    而 test1 front server 正好是在 80 端口,因此此 ssr 代理请求作为普通的请求将再次被代理,此时能正确的拿到结果。
    再次部署 test2 就出了问题:
    axios.js 生成为(端口变为3000,gogs的端口,原因不得而知):
      // baseURL
      const baseURL = process.browser
          ? '/'
          : (process.env._AXIOS_BASE_URL_ || 'http://localhost:3000/')
    因此代理结果将变为 404。
    lv1= lv2= type=

    router navigation guard

    Navigation Guards 
    created/mounted 添加 event handler 要注意,(特别是多全局对象添加),因为可能被多次执行,造成多次回调。
    本质是声明周期不一致造成的(组件的生命周期 和 $router 的生命周期不一致,后者为全局的)
    方法1:
    mounted() {
      this.$router.afterEach(this.routerHook);
    }
    beforeDestroy() {
      let i = this.$router.afterHooks.indexOf(this.routerHook);
      if (i !== -1) {
        this.$router.afterHooks.splice(i, 1);
      }
    },
    methods: {
      routerHook() {
      },
    }
    SHA-1: 4af32e7677c5e0092b18400b51ec092dc9e0b9e9
    * unregister router hook -- beforeDestory 方案
    方法2:
    watch: {
      $route() {}
    }

    轮播图 盗链 缓存问题

    轮播图 不要设置第三方网站的链接,否则其他人访问时可能会出现 403
        mounted() {
          document.getElementById("dialog-img-box").addEventListener("touchmove", (e) => {
            e.stopPropagation();
          });
          // 监听滚动事件
          window.addEventListener('scroll', this.onScroll);

    blur 时输入丢失问题

    focus 变化导致组件更新,input value 依赖从父组件中传入的 prop,如果此 prop 没有反向更新到父组件,则在更新时 value 数据丢失。
    SHA-1: d60b0362fa80b0f3e214f37c68abe602fe117066
    * blur ok

    input 点击问题;padding problem

    E:\uninote\记录视频\fe\padding-problem.mp4
    SHA-1: 4f35d65a6734e13f550a62f5a3ae4ced9e9f67d7
    * [fix]: input 点击问题

    loading 闪烁 问题

    禁用loading动画更明显看出来
    v-loading:这个会在附属的元素上居中,因此当元素比较高时,就看不到了,而且会随着元素的height而变化位置,不好。
    暂时取消 loading 动画,但保留 loading 状态。
    SHA-1: 589e3e8668e912b9cf8e0bf1efbd8225607e6d74
    * [chg]: loading 优化:避免无结果的闪烁;取消 loading 动画,但保留 loading 状态(通过顶部进度条指示 loading)。
    590a27a3d84d3dc163749a1d6218df03292448f9..589e3e8668e912b9cf8e0bf1efbd8225607e6d74
    lv1= lv2= type=

    npx 启动问题

    sf.sh & sfp.sh 
    一台机器上build的结果,在另一台机器上 npx nuxt start 不行?原因未知。用这个:
    node node_modules/nuxt/bin/nuxt.js start
    pm2 则改为:
    pm2 start node -- node_modules/nuxt/bin/nuxt.js start
    -- 共三种启动方式
    lv1= lv2= type=

    搜索

    es 搜索 api 
    focus 时全选
    快捷键搜索
    更多判定:不能根据total 判定,因为存在 es 中的结果,没有 mysql 对应数据的问题。实际展示结果比 es 返回结果少。
    最终使用:如果返回的数组长度为0,则认为没有更多了
    export const processArticles = (article) => {
          // 如果搜出来的文章没有对应的信息(_artinfo === []),则证明是 es 中的垃圾数据,忽略掉即可
    lv1= lv2= type=

    图片转储

    http://uninote.com.cn/book/1071710306/1826
    junction __pic E:\uninote\mynote\basic\web\docs\__pic
    const scriptjs = process.client ? require('scriptjs') : '';
    // Import and Set Nuxt.js options
    let config = require('../nuxt.config.js')
    config.dev = !(process.env.NODE_ENV === 'production')
    ![](/__pic/QypilpbC3ns1.png)
    ![](/__pic/xF4Ur9RY2kiu.png)
    http://localhost:3000/__pic/QypilpbC3ns1.png
    ![](http://localhost:3000/__pic/QypilpbC3ns1.png)
    ![](http://localhost:3000/__pic/QypilpbC3ns1.png)
    ![](https://user-gold-cdn.xitu.io/2019/11/11/16e5ad3e233da76a?w=164&h=80&f=png&s=2862)
    只会替换一次:
    ![](http://localhost:3000/__pic/QypilpbC3ns1.png)
    ![](http://localhost:3000/__pic/QypilpbC3ns1.png)
    lv1= lv2= type=
    * [opt]: 图片转储优化:尽量使用半绝对路径(不带host,以"/"开头的路径)
    SHA-1: 4dc52e5236b8aa4c99238c41b70ccd9384cd7287
    lv1= lv2= type=todo
    todo:一次性全部替换,但是正则干扰?
    需要取当时的 str:
    str = str.replace(url, res);
    E:\uninote\mynote-online-data\2019-11-10-迁移\docs\1079089832\随笔\maven私服搭建与jar包上传和下载.md
    ![](http://on.rongyipiao.com//link/take/lib/php/../uploads/foh1ngUI.png)
    lv1= lv2= type=

    标签嵌套问题

    使用 nuxt-link 后重复:
    vue.runtime.esm.js:619 [Vue warn]: The client-side rendered virtual DOM tree is not matching server-rendered content. This is likely caused by incorrect HTML markup, for example nesting block-level elements inside <p>, or missing <tbody>. Bailing hydration and performing full client-side render.
    a 标签不能嵌套:
        <a href="https://www.google.com" display="block">
            <span>google</span>
            <div>
                <a href="https://www.baidu.com" display="block">baidu</a>
            </div>
        </a>
    确实需要两个 a 标签,可以这样处理:
    SHA-1: d1ef591078055b6579cd38698c8eadb2fbed595f
    * [chg]: 头像链接到个人主页
    使用 absolute + 占位符
    lv1= lv2= type=

    没有使用 ssr 造成的闪烁问题

    E:\uninote\记录视频\fe\ssr-problem.mp4
    ssr的内容是不带登录信息的,因此会在用户信息从 localstorage 中加载后变化(闪烁)
    lv1= lv2= type=
    在 ssr 时就加载登录信息(asyncData)
    * [opt]: auth/getUserInfo 改为从接口获取数据,并保证仅调用接口一次;首页使用 ssr 方式加载
    lv1= lv2= type=

    domEvent scrollLoad 触底加载

    lodash 
    SHA-1: 3a7f43bdd9c1795f75b172cdcc0cd07039d756c9
    * opt: 提取 domEvent & scrollLoad
    ...
    SHA-1: c1f94b5aa9f22fd409a07719f7ec9d92db7fa54e
    * opt: 首页 loading 优化
    在 loading 完成前,多次触底只会触发一次 loading
    注意返回 promise(如果需要 loading 控制)
    自动取消事件注册
    返回 loader,force load,统一 load 入口
    1 loading 过滤
    2 scroll 高度计算,触底或者 force 才会 throttle,
    3 throttle 过滤
    lv1= lv2= type=

    iconfont

    路径 ~ 
    iconfont branch:
    a3598c847cf2db3285983724d1e32cfedd27a6ed 
    4 种方案,第二种最优,下载下来后修改最小
    第二种方案优化:
    SHA-1: 96f106a460c557649f66e21bd2404c413ca7b439
    * min 3
    只需要修改一处:
    https://uninote.com.cn/book/1027/4321#h-iconfont%202-1

    注意

    @import 相对于主文件的路径??
    styl 改为 css 后缀就不行了,后缀名对 loader 有影响

    新增、修改

    之前的项目找不到了,只能新增

    图标串了:

    http://video.dajxyl.com/video_play.html?video_url=http://admin.dajxyl.com/oss?path=video/upload/202203/20220305_211236.mp4
    lv1= lv2= type=

    login 状态检查,checkNotLogin checkLogin 需要配合 ssr

    lv1= lv2= type=

    getUserInfo getMyInfo 获取用户信息

    this.$store.dispatch('auth/getUserInfo'); 在所有 layout、需要ssr 的地方(asyncData)中调用
    不用 ssr,则或导致头像部分闪烁
    localStorage 没有使用,仅依赖 php cookie 保持登录状态
    lv1= lv2= type=

    el-dialog lock-scroll 问题 + html 样式问题

    el-dialog 
    html
      overflow: auto;
    搜索 message:lock scroll
    测试点:
    editor 是否正常撑起
    注册页面
    头像下拉菜单
    右键菜单:
    http://zentao.uninote.com.cn/zentao/task-view-15.html
    http://zentao.uninote.com.cn/zentao/task-view-16.html
    http://zentao.uninote.com.cn/zentao/task-view-48.html
    el-popup-parent--hidden
    close-on-click-modal

    html 样式问题研究 2021-7-2

    branch:html-style
    SHA-1: 540931a220a687d90fbda0e9824d8c1e162dde4f
    * html scoped 尝试,通过 xxx 动态样式实现
            const html = document.getElementsByTagName('html')[0];
            html.setAttribute('style', "height: auto; overflow: hidden");
    html 无法使用 scoped 方式局部化处理,可以通过动态设置 xxx 样式实现
    http://video.dajxyl.com/video_play.html?video_url=https://admin.dajxyl.com/oss?path=video/upload/202107/20210702_151401.mp4
    html样式的影响:
    - 自适应高度: height: 100%(编辑、注册等页面)
    - dialog lock scroll: overflow: hidden
    - dialog 不要滚动到顶部: height: auto (see 重现 dialog 滚动到顶部)
    http://video.dajxyl.com/video_play.html?video_url=https://admin.dajxyl.com/oss?path=video/upload/202107/20210702_151605.mp4
    html 元素无法实现对 body 的剪裁:
        height: 400px;
        overflow: hidden;

    axios

    axios 
    timeout:
    $axios.$post(url, data, {timeout: 1100}
    or
      $axios.defaults.timeout = 30000; // 默认超时时间
    lv1= lv2= type=

    mynote front debugger

    debugger=1 开启,仅在 default layout 中启动
    mounted() {
      this.$store.dispatch('auth/getUserInfo');
      // eruda debugger, using "?debugger=1" to start and "?debugger=0" to destroy
      const DEBUGGER_KEY = 'debugger';
      const url = new URL(location.href);
      const value = url.searchParams.get(DEBUGGER_KEY);
      if (value !== null) {
        localStorage.setItem(DEBUGGER_KEY, value);
      }
      if (localStorage.getItem(DEBUGGER_KEY) === "1") {
        const script = document.createElement('script');
        script.src = "//cdn.jsdelivr.net/npm/eruda";
        document.body.appendChild(script);
        script.onload = function () {
          eruda.init();
        }
      }
    }